#include "svdpi.h"
#include "common.h"
#include <queue>
#include <vector>
#include <string>

typedef struct
{
    svBit issue;
    svBit finish;
    svBit exeception;
    int exeception_code;
} ROB_Entry;

class ROB
{
public:
    std::vector<ROB_Entry> buffer;
    int size;
    int rptr, wptr;
    bool rflag, wflag;
    ROB(int size)
    {
        resize(size);
    }
    void resize(int size)
    {
        this->size = size;
        buffer.resize(size);
        clear();
    }
    void clear()
    {
        rptr = 0;
        wptr = 0;
        rflag = 0;
        wflag = 0;
    }
    int entry_num()
    {
        if (wflag == rflag)
        {
            return wptr - rptr;
        }
        else
        {
            return wptr + size - rptr;
        }
    }
    bool empty()
    {
        return (wflag == rflag) & (wptr == rptr);
    }
    bool full()
    {
        return (wflag != rflag) & (wptr == rptr);
    }
    int redirect(ROB_Entry &entry)
    {
        auto ptr = rptr - 1;
        if (rptr == 0)
            ptr = size - 1;
        entry = buffer[ptr];
        clear();
        return ptr;
    }
    int push(ROB_Entry entry)
    {
        Assert(!full(), "ROB Full during push new entry");
        int ptr = wptr;
        buffer[wptr] = entry;
        wptr = wptr + 1;
        if (wptr >= size)
        {
            wptr -= size;
            wflag = !wflag;
        }
        return ptr;
    }
    int pop(ROB_Entry &entry)
    {
        Assert(!empty(), "ROB empty during pop entry");
        int ptr = rptr;
        entry = buffer[rptr];
        rptr = rptr + 1;
        if (rptr >= size)
        {
            rptr -= size;
            rflag = !rflag;
        }
        return ptr;
    }
    ROB_Entry &operator[](int i)
    {
        return buffer[i];
    }
    bool check_rptr(int ptr)
    {
        return ptr == rptr;
    }
    bool check_wptr(int ptr)
    {
        return ptr == wptr;
    }
    bool verify_modif_ptr(int ptr)
    {
        if (wflag == rflag)
        {
            return ptr < wptr && ptr >= rptr;
        }
        else
        {
            return !(ptr >= wptr && ptr < rptr);
        }
    }
};
static std::vector<ROB *> robs;
static std::vector<std::string> rob_names;
static int rob_id = 0;
#ifdef __cplusplus
extern "C"
{
#endif
    int rob_create(int id, int size, const char *name)
    {
        if (id == 0)
        {
            robs.push_back(new ROB(size));
            rob_names.push_back(std::string(name));
            rob_id++;
            LOG("ROB[%d] %s created", rob_id, name);
            return rob_id;
        }
        else
        {
            auto rob = robs[id - 1];
            rob->clear();
            return id;
        }
    }
    void rob_modify(int id, int index, int ptr, svBit issue, svBit finish, svBit exeception, int exeception_code)
    {
        if (id == 0)
            return;
        auto rob = robs[id - 1];
        Assert(rob->verify_modif_ptr(ptr), "ROB %s Modify[%d] ptr %d invalid I[%d] F[%d] E[%d]", rob_names[id - 1].c_str(), index, ptr, issue, finish, exeception);
        auto &entry = (*rob)[ptr];
        if (issue)
        {
            Assert(!entry.issue, "ROB %s Modify[%d] ptr %d double issue", rob_names[id - 1].c_str(), index, ptr);
            entry.issue = issue;
        }
        if (finish)
        {
            Assert(!entry.finish, "ROB %s Modify[%d] ptr %d double finish", rob_names[id - 1].c_str(), index, ptr);
            entry.finish = finish;
        }
        if (exeception)
        {
            entry.exeception = exeception;
            entry.exeception_code = exeception_code;
        }
    }

    void rob_pop(int id, int index, int ptr, svBit issue, svBit finish, svBit exeception, int exeception_code)
    {
        if (id == 0)
            return;
        auto rob = robs[id - 1];
        Assert(rob->check_rptr(ptr), "ROB %s Pop[%d] rptr %d invalid", rob_names[id - 1].c_str(), index, ptr);
        ROB_Entry ref_entry = {0};
        int ref_ptr = rob->pop(ref_entry);
        Assert(ptr == ref_ptr, "ROB %s Pop[%d] rptr %d not equal ref %d", rob_names[id - 1].c_str(), index, ptr, ref_ptr);
        Assert(ref_entry.issue == issue, "ROB %s Pop[%d] issue %d not equal ref %d", rob_names[id - 1].c_str(), index, issue, ref_entry.issue);
        Assert(ref_entry.finish == finish, "ROB %s Pop[%d] finish %d not equal ref %d", rob_names[id - 1].c_str(), index, finish, ref_entry.finish);
        Assert(ref_entry.exeception == exeception, "ROB %s Pop[%d] exeception %d not equal ref %d", rob_names[id - 1].c_str(), index, exeception, ref_entry.exeception);
        if (exeception & ref_entry.exeception)
        {
            Assert(ref_entry.exeception_code == exeception_code, "ROB %s Pop[%d] exeception_code %d not equal ref exeception_code %d", rob_names[id - 1].c_str(), index, exeception_code, ref_entry.exeception_code);
        }
    }
    void rob_push(int id, int index, int ptr)
    {
        if (id == 0)
            return;
        auto rob = robs[id - 1];
        Assert(rob->check_wptr(ptr), "ROB %s Push[%d] wptr %d invalid", rob_names[id - 1].c_str(), index, ptr);
        int ref_ptr = rob->push({0});
        Assert(ptr == ref_ptr, "ROB %s Push[%d] wptr %d not equal ref %d", rob_names[id - 1].c_str(), index, ptr, ref_ptr);
    }
    void rob_redirect(int id, int ptr, svBit issue, svBit finish, svBit exeception, int exeception_code)
    {
        if (id == 0)
            return;
        auto rob = robs[id - 1];
        // auto ref_entry = (*rob)[ptr];
        ROB_Entry ref_entry = {0};
        auto ref_ptr = rob->redirect(ref_entry);
        Assert(ptr == ref_ptr, "ROB %s Redirect rptr %d not equal ref %d", rob_names[id - 1].c_str(), ptr, ref_ptr);
        Assert(ref_entry.issue == issue, "ROB %s Redirect issue %d not equal ref %d", rob_names[id - 1].c_str(), issue, ref_entry.issue);
        Assert(ref_entry.finish == finish, "ROB %s Redirect finish %d not equal ref %d", rob_names[id - 1].c_str(), finish, ref_entry.finish);
        Assert(ref_entry.exeception == exeception, "ROB %s Redirect exeception %d not equal ref %d", rob_names[id - 1].c_str(), exeception, ref_entry.exeception);
        if (exeception & ref_entry.exeception)
        {
            Assert(ref_entry.exeception_code == exeception_code, "ROB %s Redirect exeception_code %d not equal ref exeception_code %d", rob_names[id - 1].c_str(), exeception_code, ref_entry.exeception_code);
        }
    }

#ifdef __cplusplus
}
#endif
